/**
 * Generali Solutions d'assurances - Tous droits réservés &copy; 2007 - 2010
 */

package fr.generali.accueilclient.utils.spel.mapping.support;

import java.util.Map;

import org.springframework.core.convert.converter.Converter;

import fr.generali.accueilclient.utils.spel.mapping.Mapper;

/**
 * <p>
 * A {@link MapperBuilder} provides a fluent interface for creating a
 * {@link Mapper}. An instance of a <code>MapperBuilder</code> can be obtained
 * through the static factory methods on {@link MapperFactory} such as
 * {@link MapperFactory#mapperBuilder(Class, Class)}.
 * </p>
 * <p>
 * A <code>MappingMapperBuilder</code> provides an alternative way for creating
 * and accessing a <code>Mapper</code>. Instead of using the
 * <code>MapperFactory</code> directly, extend from this class and encapsulate
 * the knowledge necessary to prepare a specific kind of <code>Mapper</code>.
 * Internally this class maintains and delegates to an instance of a
 * <code>MapperBuilder</code> to do the actual work of creating the Mapper.
 * </p>
 * <p>
 * This class also implements the <code>Mapper</code> interface making it
 * possible to fully encapsulate the knowledge of both the definition of the
 * mappings and of the mapping itself.
 * </p>
 * <p>
 * For example given source and targget types Foo and Bar you can define a
 * MapperBuilder:
 * </p>
 * 
 * <pre>
 *    public class FooToBar extends MappingMapperBuilder<Foo,Bar> {
 *      public FooToBar() {
 *        super(Foo.class, Bar.class);
 *        super.addMapping("sourceField", "targetField");
 *    }
 * </pre>
 * <p>
 * You can then use a simple constructor to get access to FooToBar and use it as
 * a Mapper:
 * </p>
 * 
 * <pre>
 * Mapper mapper = new FooToBarMapper();
 * mapper.map(foo, bar);
 * </pre>
 * 
 * @param <S> the source type for the mapping
 * @param <T> the target type for the mapping
 * @author Rossen Stoyanchev
 */
public abstract class MappingMapperBuilder<S, T> implements MapperBuilder<S, T>, Mapper<S, T> {

    /**
     * 
     */
    private MapperBuilder<S, T> mapperBuilder;

    /**
     * @param sourceType source type
     * @param targetType target type
     */
    public MappingMapperBuilder(Class<S> sourceType, Class<T> targetType) {
        this.mapperBuilder = MapperFactory.mapperBuilder(sourceType, targetType);
    }

    /**
     * {@inheritDoc}
     */
    public Mapper<S, T> getMapper() {
        return mapperBuilder.getMapper();
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> setAutoMappingEnabled(boolean autoMappingEnabled) {
        return mapperBuilder.setAutoMappingEnabled(autoMappingEnabled);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> setExcludedFields(String... fields) {
        return mapperBuilder.setExcludedFields(fields);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> setSourceFieldDecorator(Converter<String, String> converter) {
        this.mapperBuilder.setSourceFieldDecorator(converter);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> setTargetFieldDecorator(Converter<String, String> converter) {
        this.mapperBuilder.setTargetFieldDecorator(converter);
        return this;
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addAssemblerMapping(String field, Converter< ? extends Map< ? , ? >, ? > assembler) {
        return mapperBuilder.addAssemblerMapping(field, assembler);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String field, Converter< ? , ? > converter, String condition) {
        return mapperBuilder.addConditionalMapping(field, field, converter, condition);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String field, Mapper< ? , T> mapper, String condition) {
        return mapperBuilder.addConditionalMapping(field, mapper, condition);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField,
                    Converter< ? , ? > converter, String condition) {
        return mapperBuilder.addConditionalMapping(sourceField, targetField, converter, condition);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String sourceField, String targetField, String condition) {
        return mapperBuilder.addConditionalMapping(sourceField, targetField, condition);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String field, String condition) {
        return mapperBuilder.addConditionalMapping(field, field, condition);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConditionalMapping(String[] fields, Mapper<S, T> mapper, String condition) {
        return mapperBuilder.addConditionalMapping(fields, mapper, condition);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addConverter(Converter< ? , ? > converter) {
        return mapperBuilder.addConverter(converter);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String field, Converter< ? , ? > converter) {
        return mapperBuilder.addMapping(field, field, converter);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String field, Mapper< ? , T> mapper) {
        return mapperBuilder.addMapping(field, mapper);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String sourceField, String targetField, Converter< ? , ? > converter) {
        return mapperBuilder.addMapping(sourceField, targetField, converter);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String sourceField, String targetField) {
        return mapperBuilder.addMapping(sourceField, targetField);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String field) {
        return mapperBuilder.addMapping(field, field);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addMapping(String[] fields, Mapper<S, T> mapper) {
        return mapperBuilder.addMapping(fields, mapper);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addNestedMapper(Mapper< ? , ? > nestedMapper, Converter< ? , ? > converter) {
        return mapperBuilder.addNestedMapper(nestedMapper, converter);
    }

    /**
     * {@inheritDoc}
     */
    public MapperBuilder<S, T> addNestedMapper(Mapper< ? , ? > nestedMapper) {
        return mapperBuilder.addNestedMapper(nestedMapper);
    }

    /**
     * {@inheritDoc}
     */
    public T map(S source, T target) {
        return mapperBuilder.getMapper().map(source, target);
    }

}
